'require network';
'require validation';
-var callGetCertificateFiles = rpc.declare({
+const callGetCertificateFiles = rpc.declare({
object: 'luci.openconnect',
method: 'getCertificates',
params: [ 'interface' ],
expect: { '': {} }
});
-var callSetCertificateFiles = rpc.declare({
+const callSetCertificateFiles = rpc.declare({
object: 'luci.openconnect',
method: 'setCertificates',
params: [ 'interface', 'user_certificate', 'user_privatekey', 'ca_certificate' ],
function sanitizeCert(s) {
if (typeof(s) != 'string')
- return null;
+ return '';
s = s.trim();
if (s == '')
- return null;
+ return s;
- s = s.replace(/\r\n?/g, '\n');
+ s = s.replace(/\r?\n/g, '\n');
if (!s.match(/\n$/))
s += '\n';
}
function validateCert(priv, section_id, value) {
- var beg = priv ? /^-----BEGIN (RSA )?PRIVATE KEY-----$/ : /^-----BEGIN CERTIFICATE-----$/,
- end = priv ? /^-----END (RSA )?PRIVATE KEY-----$/ : /^-----END CERTIFICATE-----$/,
- lines = value.trim().split(/[\r\n]/),
- start = false,
- i;
-
- if (value === null || value === '')
+ if (!value?.trim())
return true;
- for (i = 0; i < lines.length; i++) {
- if (lines[i].match(beg))
- start = true;
- else if (start && !lines[i].match(/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/))
- break;
- }
+ const beg = priv ? /^-----BEGIN (RSA )?PRIVATE KEY-----$/ : /^-----BEGIN CERTIFICATE-----$/;
+ const end = priv ? /^-----END (RSA )?PRIVATE KEY-----$/ : /^-----END CERTIFICATE-----$/;
+ const lines = value.trim().split(/[\r?\n]/);
+ const base64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
+ const errmsg = _('This does not look like a valid PEM file');
+
+
+ if (!lines?.[0].match(beg) || !lines.at(-1).match(end))
+ return errmsg;
- if (!start || i < lines.length - 1 || !lines[i].match(end))
- return _('This does not look like a valid PEM file');
+ for (let i = 1; i < lines.length - 1; i++)
+ if (!base64.test(lines[i]))
+ return errmsg;
return true;
}
},
renderFormOptions: function(s) {
- var dev = this.getDevice().getName(),
- certLoadPromise = null,
- o;
+ const dev = this.getDevice().getName();
+ let certLoadPromise = null;
+ let o;
o = s.taboption('general', form.ListValue, 'vpn_protocol', _('VPN Protocol'));
o.value('anyconnect', 'OpenConnect or Cisco AnyConnect SSL VPN');
o = s.taboption('general', form.Value, 'uri', _('VPN Server'));
o.placeholder = 'https://example.com:443/usergroup';
o.validate = function(section_id, value) {
- var m = String(value).match(/^(?:(\w+):\/\/|)(?:\[([0-9a-f:.]{2,45})\]|([^\/:]+))(?::([0-9]{1,5}))?(?:\/.*)?$/i);
+ const m = String(value).match(/^(?:(\w+):\/\/|)(?:\[([0-9a-f:.]{2,45})\]|([^\/:]+))(?::([0-9]{1,5}))?(?:\/.*)?$/i);
if (!m)
return _('Invalid server URL');
return certLoadPromise.then(function(certs) { return certs.user_certificate });
};
o.write = function(section_id, value) {
- return callSetCertificateFiles(section_id, sanitizeCert(value), null, null);
+ return callSetCertificateFiles(section_id, sanitizeCert(value), '', '');
};
o = s.taboption('general', form.TextValue, 'userkey', _('User key (PEM encoded)'));
return certLoadPromise.then(function(certs) { return certs.user_privatekey });
};
o.write = function(section_id, value) {
- return callSetCertificateFiles(section_id, null, sanitizeCert(value), null);
+ return callSetCertificateFiles(section_id, '', sanitizeCert(value), '');
};
o = s.taboption('general', form.TextValue, 'ca', _('CA certificate; if empty it will be saved after the first connection.'));
return certLoadPromise.then(function(certs) { return certs.ca_certificate });
};
o.write = function(section_id, value) {
- return callSetCertificateFiles(section_id, null, null, sanitizeCert(value));
+ return callSetCertificateFiles(section_id, '', '', sanitizeCert(value));
};
o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU'));
-#!/usr/bin/env ucode
-
'use strict';
import { readfile, writefile, stat } from 'fs';
const interfaceregex = /^[a-zA-Z0-9_]+$/;
-const user_certificate_string = "/etc/openconnect/user-cert-vpn-%s.pem";
-const user_privatekey_string = "/etc/openconnect/user-key-vpn-%s.pem";
-const ca_certificate_string = "/etc/openconnect/ca-vpn-%s.pem";
-
+const paths = {
+ user_certificate: "/etc/openconnect/user-cert-vpn-%s.pem",
+ user_privatekey: "/etc/openconnect/user-key-vpn-%s.pem",
+ ca_certificate: "/etc/openconnect/ca-vpn-%s.pem"
+};
-// Utility to read a file
function _readfile(path) {
- let _stat = stat(path);
- if (_stat && _stat.type == "file") {
- let content = readfile(path);
- return content ? trim(content) : 'File empty';
- }
- return 'File not found';
+ let s = stat(path);
+ return (s?.type == 'file') ? trim(readfile(path) ?? '') || 'File empty' : null;
}
-// Utility to write a file
function _writefile(path, data) {
- if (!data) {
- return false;
- }
- return writefile(path, data) == length(data);
+ return data ? writefile(path, data) == length(data) : false;
}
-const methods = {
+function is_valid_iface(ifname) {
+ return ifname && match(ifname, interfaceregex);
+}
- list:{
+const methods = {
+ list: {
call: function() {
return {
- getCertificates: {
- interface: "interface"
- },
+ getCertificates: { interface: "interface" },
setCertificates: {
interface: "interface",
user_certificate: "user_certificate",
},
getCertificates: {
- args: {
- interface: "interface",
- },
+ args: { interface: "interface" },
call: function(req) {
+ let iface = req.args?.interface;
+ if (!is_valid_iface(iface)) return;
- const _interface = req.args?.interface;
- if (!_interface || !match(_interface, interfaceregex)) {
- // printf("Invalid interface name");
- return;
- }
-
- const user_certificate_pem = _readfile(sprintf(user_certificate_string, _interface));
- const user_privatekey_pem = _readfile(sprintf(user_privatekey_string, _interface));
- const ca_certificate_pem = _readfile(sprintf(ca_certificate_string, _interface));
-
- if(user_certificate_pem && user_privatekey_pem && ca_certificate_pem){
- return {
- user_certificate: user_certificate_pem,
- user_privatekey: user_privatekey_pem,
- ca_certificate: ca_certificate_pem,
- };
- }
+ let result = {};
+ for (let k in paths)
+ result[k] = _readfile(sprintf(paths[k], iface));
+ return result;
}
},
ca_certificate: "ca_certificate",
},
call: function(req) {
+ let iface = req.args?.interface;
+ if (!is_valid_iface(iface)) return;
let result = false;
- let _interface = req.args?.interface;
-
- if (!_interface || !match(_interface, interfaceregex)) {
- // printf("Invalid interface name");
- return;
- }
-
- /* the interface is set up to call 1 write per certificate,
- with only one of the following arguments not null */
- if (req.args?.user_certificate) {
- result = _writefile(sprintf(user_certificate_string, _interface), req.args?.user_certificate);
- }
- if (req.args?.user_privatekey) {
- result = _writefile(sprintf(user_privatekey_string, _interface), req.args?.user_privatekey);
- }
- if (req.args?.ca_certificate) {
- result = _writefile(sprintf(ca_certificate_string, _interface), req.args?.ca_certificate);
+ for (let k in paths) {
+ if (req.args?.[k])
+ result = _writefile(sprintf(paths[k], iface), req.args[k]);
}
-
- return {
- result: result,
- };
-
+ return { result: result };
}
}
-
};
return { 'luci.openconnect': methods };
-